﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Xceed.Silverlight.DataGrid
{
  partial class DataGridControl
  {
    [DebuggerDisplay( "NeedsUpdate = {NeedsUpdate}" )]
    internal abstract class CellEditorVisibilityInspector
    {
      #region Constructor

      protected CellEditorVisibilityInspector()
      {
      }

      #endregion

      #region NeedsUpdate Internal Property

      internal abstract bool NeedsUpdate
      {
        get;
      }

      #endregion

      protected abstract bool VisibilityChanged();
      protected abstract bool VisibilityChanged( EditableCell cell );
      protected abstract bool VisibilityChanged( EditableRow row );
      protected abstract bool VisibilityChanged( Column column );

      internal static CellEditorVisibilityInspector Create( DataGridControl dataGrid )
      {
        return new DefaultInspector( dataGrid );
      }

      internal CellEditorVisibilityInspector Check()
      {
        if( this.NeedsUpdate || !this.VisibilityChanged() )
          return this;

        return new VisibilityChangedInspector();
      }

      internal CellEditorVisibilityInspector Check( EditableRow row )
      {
        if( row == null )
          throw new ArgumentNullException( "row" );

        if( this.NeedsUpdate || !this.VisibilityChanged( row ) )
          return this;

        return new VisibilityChangedInspector();
      }

      internal CellEditorVisibilityInspector Check( EditableRowViewModel row )
      {
        if( row == null )
          throw new ArgumentNullException( "row" );

        if( this.NeedsUpdate )
          return this;

        return this.Check();
      }

      internal CellEditorVisibilityInspector Check( EditableCell cell )
      {
        if( cell == null )
          throw new ArgumentNullException( "cell" );

        if( this.NeedsUpdate || !this.VisibilityChanged( cell ) )
          return this;

        return new VisibilityChangedInspector();
      }

      internal CellEditorVisibilityInspector Check( EditableCellViewModel cell )
      {
        if( cell == null )
          throw new ArgumentNullException( "cell" );

        if( this.NeedsUpdate )
          return this;

        return this.Check( cell.Column );
      }

      internal CellEditorVisibilityInspector Check( Column column )
      {
        if( column == null )
          throw new ArgumentNullException( "column" );

        if( this.NeedsUpdate || !this.VisibilityChanged( column ) )
          return this;

        return new VisibilityChangedInspector();
      }

      #region DefaultInspector Nested Type

      private sealed class DefaultInspector : CellEditorVisibilityInspector
      {
        internal DefaultInspector( DataGridControl dataGrid )
        {
          if( dataGrid == null )
            throw new ArgumentNullException( "dataGrid" );

          m_dataGrid = dataGrid;
        }

        internal override bool NeedsUpdate
        {
          get
          {
            return false;
          }
        }

        protected override bool VisibilityChanged()
        {
          var visitor = new RowVisitor( this );

          return visitor.VisibilityChanged( m_dataGrid );
        }

        protected override bool VisibilityChanged( EditableRow row )
        {
          return this.VisibilityChanged( row.RenderedCells );
        }

        protected override bool VisibilityChanged( EditableCell cell )
        {
          EditableCellContentElement contentElement = cell.ContentElement;
          bool isEditorVisible = ( contentElement != null )
                              && ( contentElement.ContainerType == EditableCellContentElementType.Editor );

          return ( m_dataGrid.ShowEditor( cell ) != isEditorVisible );
        }

        protected override bool VisibilityChanged( Column column )
        {
          var container = m_dataGrid.GetColumnContainer( column );

          return ( container != null )
              && ( this.VisibilityChanged( container.Cells ) );
        }

        private bool VisibilityChanged( IEnumerable<Cell> cells )
        {
          return ( from cell in cells
                   let editableCell = cell as EditableCell
                   where ( editableCell != null ) && ( this.VisibilityChanged( editableCell ) )
                   select editableCell ).Any();
        }

        private readonly DataGridControl m_dataGrid;
      }

      #endregion

      #region VisibilityChangedInspector Nested Type

      private sealed class VisibilityChangedInspector : CellEditorVisibilityInspector
      {
        internal override bool NeedsUpdate
        {
          get
          {
            return true;
          }
        }

        protected override bool VisibilityChanged()
        {
          throw new NotSupportedException();
        }

        protected override bool VisibilityChanged( EditableCell cell )
        {
          throw new NotSupportedException();
        }

        protected override bool VisibilityChanged( EditableRow row )
        {
          throw new NotSupportedException();
        }

        protected override bool VisibilityChanged( Column column )
        {
          throw new NotSupportedException();
        }
      }

      #endregion

      #region RowVisitor Nested TYpe

      private sealed class RowVisitor
      {
        internal RowVisitor( CellEditorVisibilityInspector inspector )
        {
          m_inspector = inspector;
          m_visitor = new EditableRowVisitor( ChildrenPolicy.RenderedChildrenOnly, true, this.Visit );
        }

        internal bool VisibilityChanged( DataGridControl dataGrid )
        {
          m_visibilityChanged = false;
          m_visitor.Visit( dataGrid.DataGridPanel );

          return m_visibilityChanged;
        }

        private void Visit( EditableRow row )
        {
          if( m_visibilityChanged || ( row == null ) )
            return;

          if( m_inspector.VisibilityChanged( row ) )
          {
            m_visibilityChanged = true;
            m_visitor.StopVisit();
          }
        }

        private readonly CellEditorVisibilityInspector m_inspector;
        private readonly EditableRowVisitor m_visitor;
        private bool m_visibilityChanged;
      }

      #endregion
    }
  }
}
